﻿--[[
	This file is part of FlexBar3.

	FlexBar3 is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	FlexBar3 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with FlexBar3.  If not, see <http://www.gnu.org/licenses/>.
]]
-- This class is the very base of every button in flexbar, it declares its most basic behaviour AND MUST ALWAYS BE THE FIRST CLASS TO BE LOADED. (this shouldn't usualy be a problem, as this file is loaded right after Button.lua)
-- Local upvalues
local type = type;
local rawget = rawget;
local Button = FlexBar3:GetModule("Button");
local SecureStateDriverFrame = Button.SecureStateDriverFrame;
local ClassList = Button.ClassList;

local StateMetaTable = 
{
	__index = function(tbl, key)
		return rawget(rawget(rawget(tbl, 0), "DefaultState"), key);
	end
};

local DummyMetaTable =
{
	__index = function(tbl, key)
		local State = rawget(tbl, 0);
		local StateDB = rawget(tbl, -1);
		local CurrentDB = rawget(StateDB, State);
		if(CurrentDB and CurrentDB[key]) then
			return CurrentDB[key];
		else
			return StateDB.DefaultState[key];
		end
	end,
	__newindex = function(tbl, key, value)
		local State = rawget(tbl, 0);
		local StateDB = rawget(tbl, -1);
		if(not rawget(StateDB, State)) then
			local NewStateTable = {};
			NewStateTable[0] = StateDB;
			setmetatable(NewStateTable, StateMetaTable);
			StateDB[State] = NewStateTable;
		end
		StateDB[State][key] = value;
	end
};

local StateDBMetaTable =
{
	__index = function(tbl, state)

		local DummyTable = {};
		DummyTable[0] = state;
		DummyTable[-1] = tbl;
		return setmetatable(DummyTable, DummyMetaTable);
	end
};

local function safecall(Function, ...)
	if(type(Function) == "function") then
		return Function(...);
	end
end

local BaseClass = Button:NewButtonClass("Base", 1);

local function RecursiveCall(FunctionName, Class, Object, ...)
	Object = Object or Class;
	safecall(rawget(Class, FunctionName), Object, ...);
	local Parents = Class.Parents;
	if(type(Parents) == "table") then
		for Key = 1, #Parents do
			local NewParent = Parents[Key];
			-- If we don't do this check, it will result in an infinite loop.
			if(NewParent ~= BaseClass) then
				RecursiveCall(FunctionName, NewParent, Object, ...);
			end
		end
	end
end

function BaseClass:Load() RecursiveCall("Load", self); end
function BaseClass:UpdateSettings() RecursiveCall("UpdateSettings", self); end
function BaseClass:SetDefaults() RecursiveCall("SetDefaults", self); end -- NB: a global SetDefaults should rarely be called. You usualy don't want to reset ALL the settings of a button
function BaseClass:LoadSettings() RecursiveCall("LoadSettings", self); end
function BaseClass:Enable(Name)
	FlexBar3:Debug(("Enabling button %s"):format(Name));
	Name = Name or self.Name;
	local NewButton = false;
	if(not Button.DB.ButtonList[Name]) then
		Button.DB.ButtonList[Name] = {};
		NewButton = true;
	end
	self.DB = Button.DB.ButtonList[Name];
	if(not self.DB.StateDB) then
		self.DB.StateDB = {};
	end
	local StateDB = self.DB.StateDB;
	self.StateDB = StateDB;
	for CurrentStateName, CurrentStateDB in pairs(StateDB) do
		CurrentStateDB[0] = StateDB;
		setmetatable(CurrentStateDB, StateMetaTable);
	end
	setmetatable(self.StateDB, StateDBMetaTable);
	for i = 1, #ClassList do
		local CurrentClass = ClassList[i];
		local SavedRevision;
		if(Button.DB.ButtonClasses) then
			SavedRevision = Button.DB.ButtonClasses[CurrentClass.Name];
		end
		FlexBar3:Debug(("Revision update for %s: %d -> %d"):format(CurrentClass.Name, SavedRevision or 0, CurrentClass.Revision));
		-- If there is no saved revision, if the saved revision is older then the new one or if the button is a newly created button we reset the database
		if(type(SavedRevision) ~= "number" or NewButton) then
			FlexBar3:Debug(("Ressetting the settings of %s"):format(CurrentClass.Name));
			if(CurrentClass.SetDefaults) then CurrentClass.SetDefaults(self); end
		elseif(SavedRevision < CurrentClass.Revision) then
			-- Attempt to update the settings, if it doesn't succeed we reset the database
			FlexBar3:Debug(("Updating the settings of %s"):format(CurrentClass.Name));
			if(type(CurrentClass.UpdateSettings) ~= "function" or CurrentClass.UpdateSettings(self) ~= true) then
				if(CurrentClass.SetDefaults) then CurrentClass.SetDefaults(self); end
				FlexBar3:Debug(("Ressetting the settings of %s"):format(CurrentClass.Name));
			end
		end
	end
	self:LoadSettings();
	RecursiveCall("Enable", self);
end

function BaseClass:Disable()
	RecursiveCall("Disable", self);
	self.DB = nil;
	self.Name = nil;
end

function BaseClass:Initialize(Name)
	-- If false is returned as name we abort initializing and just return the uninitialized object
	if(Name == false) then return self; end
	FlexBar3:Debug(("New button: %s"):format(Name));
	self.Name = Name;
	self:Load();
	self:Enable(Name);
	return self;
end

BaseClass.__construct = BaseClass.Initialize;
